home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / GADFLY.PY < prev    next >
Encoding:
Python Source  |  1999-07-28  |  13.4 KB  |  404 lines

  1.  
  2. """main entry point for gadfly sql."""
  3.  
  4. import sqlgen, sqlbind
  5. sql = sqlgen.getSQL()
  6. sql = sqlbind.BindRules(sql)
  7.  
  8. error = "gadfly_error"
  9. verbosity = 0
  10.  
  11. class gadfly:
  12.    """as per the DBAPI spec "gadfly" is the connection object."""
  13.    closed = 0
  14.    verbose = verbosity # debug!
  15.    
  16.    def __init__(self, databasename=None, directory=None, 
  17.          forscratch=0, autocheckpoint=1, verbose=0):
  18.        verbose = self.verbose = self.verbose or verbose
  19.        # checkpoint on each commit if set
  20.        self.autocheckpoint = autocheckpoint
  21.        if verbose:
  22.            print "initializing gadfly instance", (\
  23.               databasename, directory, forscratch, verbose)
  24.        self.is_scratch = forscratch
  25.        self.databasename=databasename
  26.        self.directory = directory
  27.        self.fs = None
  28.        self.database = None
  29.        # db global transaction id
  30.        self.transid = 0
  31.        if databasename is not None:
  32.           self.open()
  33.           
  34.    def transaction_log(self):
  35.        from gfdb0 import Transaction_Logger
  36.        if self.verbose:
  37.           print "new transaction log for", self.transid
  38.        return Transaction_Logger(self.database.log, self.transid, self.is_scratch)
  39.           
  40.    # causes problems in 1.5?
  41.    #def __del__(self):
  42.    #    """clear database on deletion, just in case of circularities"""
  43.    #    # implicit checkpoint
  44.    #    if self.verbose:
  45.    #        print "deleting gadfly instance", self.databasename
  46.    #    if not self.closed:
  47.    #        self.close()
  48.        
  49.    def checkpoint(self):
  50.        """permanently record committed updates"""
  51.        # note: No transactions should be active at checkpoint for this implementation!
  52.        # implicit abort of active transactions!
  53.        verbose = self.verbose
  54.        if verbose:
  55.            print "checkpointing gadfly instance", self.databasename
  56.        db = self.database
  57.        log = db.log
  58.        # dump committed db to fs
  59.        fs = self.fs
  60.        if fs and db and not db.is_scratch:
  61.            # flush the log
  62.            if log: 
  63.                if verbose: print "gadfly: committing log"
  64.                log.commit()
  65.            elif verbose:
  66.                print "gadfly: no log to commit"
  67.            if verbose: print "gadfly: dumping mutated db structures"
  68.            fs.dump(db)
  69.        elif verbose:
  70.            print "gadfly: no checkpoint required"
  71.        if verbose:
  72.            print "gadfly: new transid, reshadowing"
  73.        self.transid = self.transid+1
  74.        self.working_db.reshadow(db, self.transaction_log())
  75.           
  76.    def startup(self, databasename, directory, scratch=0, verbose=0):
  77.        from gfdb0 import Database0, File_Storage0
  78.        verbose = self.verbose
  79.        if verbose:
  80.            print "gadfly: starting up new ", databasename
  81.        if self.database:
  82.            raise error, "cannot startup, database bound"
  83.        self.databasename=databasename
  84.        self.directory = directory
  85.        db = self.database = Database0()
  86.        db.is_scratch = scratch or self.is_scratch
  87.        self.fs = File_Storage0(databasename, directory)
  88.        self.working_db = Database0(db, self.transaction_log())
  89.        # commit initializes database files and log structure
  90.        self.commit()
  91.        # for now: all transactions serialized
  92.        #  working db shared among all transactions/cursors
  93.        self.transid = self.transid+1
  94.        self.working_db = Database0(db, self.transaction_log())
  95.        
  96.    def restart(self):
  97.        """reload and rerun committed updates from log, discard uncommitted"""
  98.        # mainly for testing recovery.
  99.        if self.verbose:
  100.            print "gadfly: restarting database", self.databasename
  101.        self.database.clear()
  102.        self.working_db.clear()
  103.        self.working_db = None
  104.        self.database = None
  105.        self.open()
  106.           
  107.    def open(self):
  108.        """(re)load existing database"""
  109.        if self.verbose:
  110.            print "gadfly: loading database", self.databasename
  111.        from gfdb0 import File_Storage0, Database0
  112.        if self.directory:
  113.            directory = self.directory
  114.        else:
  115.            directory = "."
  116.        fs = self.fs = File_Storage0(self.databasename, directory)
  117.        db = self.database = fs.load(sql)
  118.        self.transid = self.transid+1
  119.        self.working_db = Database0(db, self.transaction_log())
  120.        
  121.    def add_remote_view(self, name, definition):
  122.        """add a remote view to self.
  123.           Must be redone on each reinitialization!
  124.           Must not recursively reenter the query evaluation process for
  125.           this database!
  126.           "Tables" added in this manner cannot be update via SQL.
  127.        """
  128.        self.database[name] = definition
  129.        self.working_db[name] = definition
  130.        
  131.    def close(self):
  132.        """checkpoint and clear the database"""
  133.        if self.closed: return
  134.        if self.verbose:
  135.            print "gadfly: closing database", self.databasename
  136.        db = self.database
  137.        if not db.is_scratch:
  138.            self.checkpoint()
  139.        if db: db.clear()
  140.        wdb = self.working_db
  141.        if wdb:
  142.            wdb.clear()
  143.        self.working_db = None
  144.        self.closed = 1
  145.        
  146.    def commit(self):
  147.        """commit the working database+transaction, flush log, new transid"""
  148.        verbose = self.verbose
  149.        autocheckpoint = self.autocheckpoint
  150.        if self.verbose:
  151.            print "gadfly: committing", self.transid, self.databasename
  152.        self.transid = self.transid+1
  153.        fs = self.fs
  154.        db = self.database
  155.        wdb = self.working_db
  156.        wdblog = wdb.log
  157.        if wdblog: wdblog.commit()
  158.        wdb.commit()
  159.        if fs and db and not db.is_scratch:
  160.           if autocheckpoint:
  161.               if verbose:
  162.                   print "gadfly: autocheckpoint"
  163.               # skips a transid?
  164.               self.checkpoint()
  165.           else:
  166.               if verbose:
  167.                   print "gadfly: no autocheckpoint"
  168.               wdb.reshadow(db, self.transaction_log())
  169.        else:
  170.           if verbose:
  171.               print "gadfly: scratch db, no logging, just reshadow"
  172.           wdb.reshadow(db, self.transaction_log())
  173.           
  174.    def rollback(self):
  175.        """discard the working db, new transid, recreate working db"""
  176.        verbose = self.verbose
  177.        if verbose:
  178.            print "gadfly: rolling back", self.transid, self.databasename
  179.        if not (self.fs or self.database):
  180.           raise error, "unbound, cannot rollback"
  181.        # discard updates in working database
  182.        self.working_db.clear()
  183.        self.transid = self.transid+1
  184.        self.working_db.reshadow(self.database, self.transaction_log())
  185.        #self.open()
  186.        
  187.    def cursor(self):
  188.        if self.verbose:
  189.            print "gadfly: new cursor", self.databasename
  190.        db = self.database
  191.        if db is None:
  192.           raise error, "not bound to database"
  193.        return GF_Cursor(self)
  194.        
  195.    def dumplog(self):
  196.        log = self.database.log
  197.        if log:
  198.            log.dump()
  199.        else:
  200.            print "no log to dump"
  201.            
  202.    def table_names(self):
  203.        return self.working_db.relations()
  204.        
  205.    def DUMP_ALL(self):
  206.        print "DUMPING ALL CONNECTION DATA", self.databasename, self.directory
  207.        print
  208.        print "***** BASE DATA"
  209.        print
  210.        print self.database
  211.        print
  212.        print "***** WORKING DATA"
  213.        print
  214.        print self.working_db
  215.        
  216.  
  217. class GF_Cursor:
  218.  
  219.    verbose = verbosity
  220.  
  221.    arraysize = None
  222.    
  223.    description = None
  224.    
  225.    EVAL_DUMP = 0 # only for extreme debugging!
  226.    
  227.    def __init__(self, gadfly_instance):
  228.        verbose = self.verbose = self.verbose or gadfly_instance.verbose
  229.        if verbose:
  230.            print "GF_Cursor.__init__", id(self)
  231.        self.connection = gadfly_instance
  232.        self.results = None
  233.        self.resultlist = None
  234.        self.statement = None
  235.        # make a shadow of the shadow db! (in case of errors)
  236.        from gfdb0 import Database0
  237.        self.shadow_db = Database0()
  238.        self.reshadow()
  239.        self.connection = gadfly_instance
  240.        
  241.    def reshadow(self):
  242.        if self.verbose:
  243.           print "GF_Cursor.reshadow", id(self)
  244.        db = self.connection.working_db
  245.        shadow = self.shadow_db
  246.        shadow.reshadow(db, db.log)
  247.        if self.verbose:
  248.           print "rels", shadow.rels.keys()
  249.        
  250.    def close(self):
  251.        if self.verbose:
  252.            print "GF_Cursor.close", id(self)
  253.        self.connection = None
  254.        
  255.    def reset_results(self):
  256.        if self.verbose:
  257.            print "GF_Cursor.reset_results", id(self)
  258.        rs = self.results
  259.        if rs is None:
  260.            raise error, "must execute first"
  261.        if len(rs)!=1:
  262.            raise error, "cannot retrieve multiple results"
  263.        rel = rs[0]
  264.        rows = rel.rows()
  265.        atts = rel.attributes()
  266.        tupatts = tuple(atts)
  267.        resultlist = list(rows)
  268.        if len(tupatts)==1:
  269.            att = tupatts[0]
  270.            for i in xrange(len(resultlist)):
  271.               resultlist[i] = (resultlist[i][att],)
  272.        else:
  273.            for i in xrange(len(resultlist)):
  274.               resultlist[i] = resultlist[i].dump(tupatts)
  275.        self.resultlist = resultlist
  276.        
  277.    def fetchone(self):
  278.        if self.verbose:
  279.            print "GF_Cursor.fetchone", id(self)
  280.        r = self.resultlist
  281.        if r is None:
  282.           self.reset_results()
  283.           r = self.resultlist
  284.        if len(r)<1:
  285.           raise error, "no more results"
  286.        result = r[0]
  287.        del r[0]
  288.        return result
  289.        
  290.    def fetchmany(self, size=None):
  291.        if self.verbose:
  292.            print "GF_Cursor.fetchmany", id(self)
  293.        r = self.resultlist
  294.        if r is None:
  295.            self.reset_results()
  296.            r = self.resultlist
  297.        if size is None:
  298.            size = len(r)
  299.        result = r[:size]
  300.        del r[:size]
  301.        return result
  302.        
  303.    def fetchall(self):
  304.        if self.verbose:
  305.            print "GF_Cursor.fetchall", id(self)
  306.        return self.fetchmany()
  307.        
  308.    def execute(self, statement=None, params=None):
  309.        """execute operations, commit results if no error"""
  310.        success = 0
  311.        verbose = self.verbose
  312.        if verbose:
  313.            print "GF_Cursor.execute", id(self)
  314.        if statement is None and self.statement is None:
  315.            raise error, "cannot execute, statement not bound"
  316.        if statement!=self.statement:
  317.            if verbose: print "GF_cursor: new statement: parsing"
  318.            # only reparse on new statement.
  319.            self.statement=statement
  320.            from sqlsem import Parse_Context
  321.            context = Parse_Context()
  322.            cs = self.commands = sql.DoParse1(statement, context)
  323.        else:
  324.            if verbose: print "GF_cursor: old statment, not parsing"
  325.            cs = self.commands
  326.        # always rebind! (db may have changed)
  327.        if verbose: print "GF_Cursor: binding to temp db"
  328.        # make a new shadow of working db
  329.        # (should optimize?)
  330.        self.reshadow()
  331.        # get shadow of working database
  332.        database = self.shadow_db
  333.        if self.EVAL_DUMP:
  334.            print "***"
  335.            print "*** dumping connection parameters before eval"
  336.            print "***"
  337.            print "*** eval scratch db..."
  338.            print
  339.            print database
  340.            print
  341.            print "*** connection data"
  342.            print
  343.            self.connection.DUMP_ALL()
  344.            print "********** end of eval dump"
  345.        for i in xrange(len(cs)):
  346.            if verbose:
  347.               print "GFCursor binding\n", cs[i]
  348.               print database.rels.keys()
  349.            cs[i] = cs[i].relbind(database)
  350.        cs = self.commands
  351.        self.results = results = list(cs)
  352.        # only unshadow results on no error
  353.        try:
  354.            for i in xrange(len(cs)):
  355.                results[i] = cs[i].eval(params)
  356.            success = 1
  357.        finally:
  358.            #print "in finally", success
  359.            # only on no error...
  360.            if success:
  361.                # commit updates in shadow of working db (not in real db)
  362.                if verbose: print "GFCursor: successful eval, storing results in wdb"
  363.                database.log.flush()
  364.                # database commit does not imply transaction commit.
  365.                database.commit()
  366.            else:
  367.                if verbose:
  368.                    print \
  369.    "GFCursor: UNSUCCESSFUL EVAL, discarding results and log entries"
  370.                self.statement = None
  371.                self.results = None
  372.                self.resultlist = None
  373.                database.log.reset()
  374.        # handle curs.description
  375.        self.description = None
  376.        if len(results)==1:
  377.           result0 = results[0]
  378.           try:
  379.               atts = result0.attributes()
  380.           except:
  381.               pass
  382.           else:
  383.               descriptions = list(atts)
  384.               fluff = (None,) * 6
  385.               for i in xrange(len(atts)):
  386.                   descriptions[i] = (atts[i],) + fluff
  387.               self.description = tuple(descriptions)
  388.        self.resultlist = None
  389.        
  390.    def setoutputsize(self, *args):
  391.        # not implemented
  392.        pass
  393.        
  394.    def setinputsizes(self, *args):
  395.        # not implemented
  396.        pass
  397.            
  398.    def pp(self):
  399.        """return pretty-print string rep of current results"""
  400.        from string import join
  401.        stuff = map(repr, self.results)
  402.        return join(stuff, "\n\n")
  403.        
  404.